// Copyright (C) 2024 Kumo inc.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
#pragma once

#include <cstddef>
#include <iterator>
#include <tuple>
#include <utility>

namespace deneb::internal {

    /// Generates an index sequence for a tuple.
    ///
    /// \tparam Ts The types of the tuple (to deduce the size).
    template <typename... Ts>
    constexpr auto tuple_indices(const std::tuple<Ts...>&) {
      return std::make_index_sequence<sizeof...(Ts)>();
    }

    /// Applies (in the functional sense) a tuple to the constructor of a class.
    ///
    /// \tparam T The type to construct.
    /// \tparam Indices The indices into the tuple (generated from an index
    ///                 sequence).
    /// \param args The tuple of arguments to construct the object with.
    template <typename T, typename... Args, std::size_t... Indices>
    constexpr T construct_from_tuple(const std::tuple<Args...>& arguments,
                                     std::index_sequence<Indices...>) {
      return T(std::forward<Args>(std::get<Indices>(arguments))...);
    }

    /// Applies (in the functional sense) a tuple to the constructor of a class.
    ///
    /// \tparam T The type to construct.
    /// \param args The tuple of arguments to construct the object with.
    template <typename T, typename... Args>
    constexpr T construct_from_tuple(const std::tuple<Args...>& args) {
      return construct_from_tuple<T>(args, tuple_indices(args));
    }

    /// Applies (in the functional sense) a tuple to the constructor of a class.
    ///
    /// \tparam T The type to construct.
    /// \param args The tuple of arguments to construct the object with.
    template <typename T, typename... Args>
    constexpr T construct_from_tuple(std::tuple<Args...>&& args) {
      return construct_from_tuple<T>(std::move(args), tuple_indices(args));
    }

    /// A type trait that disables a template overload if a type is not an iterator.
    ///
    /// \tparam T the type to check.
    template <typename T>
    using enable_if_iterator = typename std::iterator_traits<T>::value_type;

    /// A type trait that disables a template overload if a type is not a range.
    ///
    /// \tparam T the type to check.
    template <typename T>
    using enable_if_range = std::pair<decltype(std::declval<T>().begin()),
                                      decltype(std::declval<T>().end())>;

    /// A type trait that disables a template overload if a type is not an iterator
    /// over a pair.
    ///
    /// \tparam T the type to check.
    template <typename T>
    using enable_if_iterator_over_pair =
        std::pair<typename std::iterator_traits<T>::value_type::first_type,
                  typename std::iterator_traits<T>::value_type::first_type>;


    /// A type trait that disables a template overload if a type is not convertible
    /// to a target type.
    ///
    /// \tparam Target The type one wants to check against.
    /// \tparam T The type to check.
    template <typename Target, typename T>
    using enable_if_same = std::enable_if_t<std::is_convertible<T, Target>::value>;

    /// Base case for `static_all_of` (the neutral element of AND is true).
    constexpr bool static_all_of() noexcept {
      return true;
    }

    /// Checks if all the given parameters evaluate to true.
    ///
    /// \param head The first expression to check.
    /// \param tail The remaining expression to check.
    template <typename Head, typename... Tail>
    constexpr bool static_all_of(Head&& head, Tail&&... tail) {
      // Replace with (ts && ...) when the time is right
      return std::forward<Head>(head) && static_all_of(std::forward<Tail>(tail)...);
    }

    /// Base case for `static_any_of` (the neutral element of OR is false).
    constexpr bool static_any_of() noexcept {
      return false;
    }

    /// Checks if any the given parameters evaluate to true.
    ///
    /// \param head The first expression to check.
    /// \param tail The remaining expression to check.
    /// \returns True if any of the given parameters evaluate to true.
    template <typename Head, typename... Tail>
    constexpr bool static_any_of(Head&& head, Tail&&... tail) {
      // Replace with (ts || ...) when the time is right
      return std::forward<Head>(head) || static_any_of(std::forward<Tail>(tail)...);
    }

    /// Checks if none the given parameters evaluate to true.
    ///
    /// \param ts The expressions to check.
    /// \returns True if any of the given parameters evaluate to true.
    template <typename... Ts>
    constexpr bool static_none_of(Ts&&... ts) {
      // Replace with (!ts && ...) when the time is right
      return !static_any_of(std::forward<Ts>(ts)...);
    }

    /// Checks if all the given types are convertible to the first type.
    ///
    /// \tparam T the first type.
    /// \tparam Ts The types to check against the first.
    template <typename T, typename... Ts>
    constexpr bool
        all_of_type = static_all_of(std::is_convertible<Ts, T>::value...);

    /// Checks if none of the given types are convertible to the first type.
    ///
    /// \tparam T the first type.
    /// \tparam Ts The types to check against the first.
    template <typename T, typename... Ts>
    constexpr bool
        none_of_type = static_none_of(std::is_convertible<Ts, T>::value...);

    /// Base case for `for_each`.
    template <typename Function>
    void for_each(Function) noexcept {
    }

    /// Calls a function for each of the given variadic arguments.
    ///
    /// \param function The function to call for each argument.
    /// \param head The first value to call the function with.
    /// \param tail The remaining values to call the function with.
    template <typename Function, typename Head, typename... Tail>
    void for_each(Function function, Head&& head, Tail&&... tail) {
      function(std::forward<Head>(head));
      for_each(function, std::forward<Tail>(tail)...);
    }

}  // namespace namespace deneb::internal
